from unittest.case import TestCase
from unittest.loader import TestLoader
from unittest.runner import TextTestRunner
from tpFinal.tpFinal11 import Kernel,OutOfMemoryBoundsException,MemoryBlock,MMUAsignacionContinua 
from tpFinal.tpFinal11 import Memory,firstFit,bestFit,worstFit,Prog,Instr
from tpFinal.tpFinal11 import FIFOScheduler,PriorityScheduler,RoundRobin,CPU,Timer
from tpFinal.tpFinal11 import PCB,RemoveBlockFailException,NotFoundProgramInMemoryException
from tpFinal.tpFinal11 import MMUPaginacion,FIFO,LRU,LFU,MFU,pila,Pagina,Marco
import time


class TestKernel(TestCase):    
    
    def setUp(self):
        self.kernel = Kernel()        
        self.kernel.mmu = MockMMU()
        self.kernel.scheduler = MockScheduler()
        self.instr0 = Instr('CPU', 50)
        self.instr1 = Instr('CPU', 50)
        self.instr2 = Instr('IO', 50)
        self.prog = Prog([self.instr0,self.instr1,self.instr2],'CERO')
        
    def testExpandCPUInstr(self):        
        expanded = self.kernel.expandCPUInstr(self.instr0)
        self.assertEquals(len(expanded),50)
        
    def testExpandCPUInstrs(self):
        expanded = self.kernel.expandCPUInstrs(self.prog)
        self.assertEquals(len(expanded.code),101)   
        
    def testUpdatePCB(self):
        pcb = PCB('progName', 1, 50, 20, 69 , 5)
        block = MemoryBlock(50, 10, 59)
        self.kernel.pcbt[1] = pcb        
        
        self.kernel.updatePCB(block, 40)
        
        self.assertEquals(self.kernel.pcbt[1].base,10)
        
    def testProgMemoryOutToDisk(self):
        self.kernel.processesInMemory.append(1)
        self.kernel.pcbt[1] = PCB('progName', 1, 50, 20, 69 , 5)
        try:
            self.kernel.progMemoryOutToDisk(2)
            self.fail()
        except ValueError:
            self.kernel.progMemoryOutToDisk(1)
            
            self.assertEqual(len(self.kernel.processesInMemory),0)
            self.assertEqual(len(self.kernel.processesInDisk),1)
            self.assertEqual(self.kernel.mmu.count1,1)
            
    def testReSwapping(self):        
        self.kernel.processesInDisk.append(0)
        self.kernel.run(self.prog)
        
        self.kernel.reSwapping(0)
        
        self.assertEqual(self.kernel.pcbt[0].base,1)
        self.assertEqual(self.kernel.pcbt[0].limit,2)
        self.assertEqual(self.kernel.pcbt[0].length,0)
        self.assertEqual(self.kernel.pcbt[0].progName,'name')
        self.assertEqual(len(self.kernel.processesInDisk),0)
        self.assertEqual(len(self.kernel.processesInMemory),2)
        
    def testReSwapping2(self):
        self.kernel.processesInDisk.append(1)
        self.kernel.pcbt[1] = PCB('progName', 1, 50, 20, 69 , 5,3)
        
        self.kernel.reSwapping(1)
        
        self.assertEqual(self.kernel.pcbt[1].pc,3)
        
        
        
    def testRun(self):
        self.kernel.run(self.prog)
        
        self.assertEqual(len(self.kernel.pcbt.keys()),1)
        self.assertEqual(self.kernel.pcbt[0].length,101)
        self.assertEqual(len(self.kernel.processesInMemory),1)
        self.assertEqual(len(self.kernel.processesInDisk),0)
        self.assertEqual(self.kernel.scheduler.verify,1)
        
    def testSearchMinPriorityPidInMemory(self):
        self.kernel.run(self.prog,5)
        self.kernel.run(self.prog,1)
        self.kernel.run(self.prog,4)
        self.kernel.run(self.prog,2)
        self.kernel.pcbt[4] = PCB('progName', 1, 50, 20, 69 , -5)
        self.kernel.ready.append(4)
        self.kernel.processesInDisk.append(4)
        
        pid = self.kernel.searchMinPriorityPidInMemory()
        
        self.assertEqual(pid,0)
        
        
                
class MockMMU():
    
    def __init__(self):
        self.count1 = 0
        self.count2 = 0
        self.count3 = 0
        
        
    def progMemoryOutToDisk(self,name,base):
        self.count1 += 1 
        
    def loadProg(self,prog):
        self.count2 += 1
        return (1,2)
    
    def getProgFromDisk(self,progName):
        self.count3 += 1
        return Prog([], 'name')

class MockScheduler(): 
    
    def __init__(self):
        self.verify = 0
        
    def execute(self):
        self.verify += 1 
                
        
        
        
class TestFIFOScheduler(TestCase):
    
    def setUp(self):
        self.kernel = Kernel()
        self.instr0 = Instr('CPU', 1)
        self.instr1 = Instr('CPU', 1)
        self.instr2 = Instr('IO', 1)
        
        self.prog0 = Prog([self.instr0,self.instr1,self.instr2],'CERO')
        self.prog1 = Prog([self.instr0,self.instr1,self.instr2],'UNO')
        self.prog2 = Prog([self.instr0,self.instr1,self.instr2],'DOS')
        self.prog3 = Prog([self.instr0,self.instr1,self.instr2],'TRES')    
        
        self.kernel.scheduler = FIFOScheduler(self.kernel)
        self.kernel.mmu = MockMMU()
        self.kernel.cpu = CPU(self.kernel, self.kernel.mmu)  
        
    def testPcbsLength(self):
        pcb = PCB('progName', 1, 0, 10, 9, 3)
        pcb1 = PCB('progName', 1, 0, 15, 9, 3)
        pcb2 = PCB('progName', 1, 0, 20, 9, 3)
        pcb3 = PCB('progName', 1, 0, 15, 9, 3)
        pcb4 = PCB('progName', 1, 0, 40, 9, 3)
        list = [pcb,pcb1,pcb2,pcb3,pcb4]
        length = self.kernel.scheduler.pcbsLength(list)
        
        self.assertEqual(length, 100)
        
    def testSwap(self):
        self.kernel.processesInMemory.append(0)
        self.kernel.processesInMemory.append(1)
        self.kernel.processesInMemory.append(2)
        self.kernel.pcbt[0] = PCB('progName', 0, 0, 2, 9, 3)
        self.kernel.pcbt[1] = PCB('progName', 1, 0, 2, 9, 3)
        self.kernel.pcbt[2] = PCB('progName', 2, 0, 2, 9, 3)
        self.kernel.ready = [0,1,2]                     
        
        self.kernel.scheduler.swap(self.prog3)
        
        self.assertEqual(len(self.kernel.processesInMemory), 1)
        self.assertEqual(len(self.kernel.processesInDisk), 2)
        self.assertEqual(self.kernel.processesInMemory[0],0)
        self.assertEqual(self.kernel.mmu.count1,2)
        self.assertEqual(self.kernel.mmu.count2,1)
        
    def testExecute(self):        
        
        self.kernel.scheduler.execute()
        self.assertEquals(self.kernel.current_pid,None)
        
        #run levanta una interrupcion del kernel y si este esta idle llama a execute del scheduler
        self.kernel.run(self.prog0)
        self.kernel.run(self.prog1)
        self.kernel.run(self.prog2)        
                
        self.assertEquals(self.kernel.current_pid,0)
        
        self.kernel.scheduler.execute()
        self.assertEquals(self.kernel.current_pid,1)
        
        self.kernel.scheduler.execute()
        self.assertEquals(self.kernel.current_pid,2)        
        
        
class TestPrioritycheduler(TestCase):
    
    def setUp(self):
        self.kernel = Kernel()
        self.instr0 = Instr('CPU', 10)
        self.instr1 = Instr('CPU', 10)
        self.instr2 = Instr('IO', 10)
        
        self.prog0 = Prog([self.instr0,self.instr1,self.instr2],'CERO')
        self.prog1 = Prog([self.instr0,self.instr1,self.instr2],'UNO')
        self.prog2 = Prog([self.instr0,self.instr1,self.instr2],'DOS')  
        
        self.kernel.scheduler = PriorityScheduler(self.kernel)
        self.kernel.mmu = MockMMU()
        self.kernel.cpu = CPU(self.kernel, self.kernel.mmu)    
        
    def testSwap(self):
        self.kernel.processesInMemory.append(0)
        self.kernel.processesInMemory.append(1)
        self.kernel.processesInMemory.append(2)
        self.kernel.pcbt[0] = PCB('progName', 0, 0, 2, 9, 10)
        self.kernel.pcbt[1] = PCB('progName', 1, 0, 2, 9, -10)
        self.kernel.pcbt[2] = PCB('progName', 2, 0, 2, 9, 0)
        self.kernel.ready = [0,1,2]                     
        
        self.kernel.scheduler.swap(self.prog0)
        
        self.assertEqual(len(self.kernel.processesInMemory), 1)
        self.assertEqual(len(self.kernel.processesInDisk), 2)
        self.assertEqual(self.kernel.processesInMemory[0],1)
        self.assertEqual(self.kernel.mmu.count1,2)
        self.assertEqual(self.kernel.mmu.count2,1)
        
    def testExecute(self):        
        self.kernel.scheduler.execute()
        self.assertEquals(self.kernel.current_pid,None)
        
        #run levanta una interrupcion del kernel y si este esta idle llama a execute del scheduler
        self.kernel.run(self.prog0,10)
        self.kernel.run(self.prog1,8)
        self.kernel.run(self.prog2,7)        
                
        self.assertEquals(self.kernel.current_pid,0)
        
        self.kernel.scheduler.execute() 
        self.assertEquals(self.kernel.current_pid,2)
        
        self.kernel.run(self.prog0,5)#este programa tendra pid 3         
        
        self.kernel.scheduler.execute() 
        self.assertEquals(self.kernel.current_pid,3)
        
        self.kernel.run(self.prog1,2)#este programa tendra pid 4       
        
        self.kernel.scheduler.execute() 
        self.assertEquals(self.kernel.current_pid,4)  
        
    def testAge(self):        
        #run levanta una interrupcion del kernel y si este esta idle llama a execute del scheduler
        self.kernel.run(self.prog0,10)
        self.kernel.run(self.prog1,8)
        self.kernel.run(self.prog2,1)  
        
        self.kernel.scheduler.age()
        self.assertEquals(self.kernel.pcbt[0].priority,10)
        self.assertEquals(self.kernel.pcbt[1].priority,7)
        self.assertEquals(self.kernel.pcbt[2].priority,0)
        
    def testNextProccess(self):
        #run levanta una interrupcion del kernel y si este esta idle llama a execute del scheduler
        self.kernel.run(self.prog0,10)#pid 0
        self.kernel.run(self.prog1,8)#pid 1
        self.kernel.run(self.prog2,5)#pid 2
        self.kernel.run(self.prog2,1)#pid 3

        nextProccessPid = self.kernel.scheduler.nextProccess()
        self.assertEquals(nextProccessPid,3)
        
class MockRoundRobin():

    def __init__(self):
        self.testVar = False

    def interrupt(self):
        self.testVar = True


class TestTimer(TestCase):

    def setUp(self):
        self.timer = Timer(MockRoundRobin(),3)
        
    def testIncCount(self):
        self.timer.incCount()
        self.assertEquals(self.timer.verify,1)

        self.timer.incCount()
        self.assertEquals(self.timer.verify,2)

        self.timer.incCount()
        self.assertEquals(self.timer.verify,3)
        self.assertTrue(self.timer.scheduler.testVar)

class TestMMUPolicys(TestCase):

    def setUp(self):        
        self.hollows = [MemoryBlock(0,10,9),MemoryBlock(10,30,39),MemoryBlock(40,40,79),
                        MemoryBlock(80,20,99)]
        self.instr = Instr('CPU', 1)     
        #El programa tiene 20 instrucciones   
        self.prog = Prog([self.instr,self.instr,self.instr,self.instr,self.instr,
                           self.instr,self.instr,self.instr,self.instr,self.instr,
                           self.instr,self.instr,self.instr,self.instr,self.instr,
                           self.instr,self.instr,self.instr,self.instr,self.instr],'CERO')

    def testFirstFit(self):
        hollow = firstFit(self.prog,self.hollows)
        self.assertEquals(hollow.base,10)
        self.assertEquals(hollow.limit,39)
        
    def testBestFit(self):
        hollow = bestFit(self.prog,self.hollows)
        self.assertEquals(hollow.base,80)
        self.assertEquals(hollow.limit,99)
        
    def testWorstFit(self):
        hollow = worstFit(self.prog,self.hollows)
        self.assertEquals(hollow.base,40)
        self.assertEquals(hollow.limit,79)




class TestMMUAsignacionContinua(TestCase):

    def setUp(self):
        self.mmu = MMUAsignacionContinua(Kernel(),firstFit)
        
        self.mmu.hollows = [MemoryBlock(0,10,9),MemoryBlock(20,10,29),
                        MemoryBlock(30,10,39),MemoryBlock(60,10,69),
                        MemoryBlock(70,10,79),MemoryBlock(80,10,89)]
        self.mmu.allocatedBlocks = [MemoryBlock(10,10,19),MemoryBlock(40,10,49),
                                    MemoryBlock(50,10,59),MemoryBlock(90,10,99)]
        self.instr = Instr('CPU', 1)
        #Programa de 15 instrucciones
        self.prog = Prog([self.instr,self.instr,self.instr,self.instr,self.instr,
                           self.instr,self.instr,self.instr,self.instr,self.instr,
                           self.instr,self.instr,self.instr,self.instr,self.instr],'CERO')       

        

    def testHollowsBeforeLength(self):
        length = self.mmu.hollowsBeforeLength(50)
        
        self.assertEquals(length,30)
        
    def testMoveUp(self):
        block = MemoryBlock(90,10,99)
        self.mmu.moveUp(block, 50)
        
        self.assertEquals(block.base,40)
        self.assertEquals(block.length,10)
        self.assertEquals(block.limit,49)
        
    def testCompact(self):
        self.mmu.compact()
        
        self.assertEquals(len(self.mmu.hollows),1)
        self.assertEquals(len(self.mmu.allocatedBlocks),4)
        self.assertEquals(self.mmu.hollows[0].length,60)
        self.assertEquals(self.mmu.allocatedBlocks[0].base,0)
        self.assertEquals(self.mmu.allocatedBlocks[1].base,10)
        self.assertEquals(self.mmu.allocatedBlocks[2].base,20)
        self.assertEquals(self.mmu.allocatedBlocks[3].base,30)
        
    def testAddHollow(self):
        block = MemoryBlock(40,20,59)
        self.mmu.addHollow(block)
        
        self.assertEquals(len(self.mmu.hollows),5)
        self.assertEquals(self.mmu.hollows[-1].length,40)
        
    def testRemoveAllocatedBlock(self):
        try:
            self.mmu.removeAllocatedBlock(5)
            self.fail()
        except RemoveBlockFailException:
            self.mmu.removeAllocatedBlock(10)
            
            self.assertEquals(len(self.mmu.allocatedBlocks),3)
            self.assertEquals(len(self.mmu.hollows),5)
            
    def testLoadProg(self):
        (base,limit) = self.mmu.loadProg(self.prog)
        
        self.assertEquals(base,40)
        self.assertEquals(limit,54)
        self.assertEquals(len(self.mmu.hollows),1)
        self.assertEquals(len(self.mmu.allocatedBlocks),5)
        self.assertEquals(self.mmu.hollows[0].length,45)
        
        self.mmu.loadProg(self.prog)
        self.mmu.loadProg(self.prog)
        self.mmu.loadProg(self.prog)
        (base,limit) = self.mmu.loadProg(self.prog)
        
        self.assertEquals(base,None)
        self.assertEquals(limit,None)
        self.assertEqual(len(self.mmu.programsInMemory),4)
        
    def testSearchProgInMemory(self):
        self.mmu.loadProg(self.prog)
        try:
            self.mmu.searchProgInMemory('Prog')
            self.fail()
        except NotFoundProgramInMemoryException:
            prog = self.mmu.searchProgInMemory('CERO')
            
            self.assertEquals(prog.name,'CERO')
            self.assertEquals(len(prog.code),15)
            
    def testProgMemoryOutToDisk(self):
        (base,limit) = self.mmu.loadProg(self.prog)
        self.mmu.progMemoryOutToDisk('CERO',base)
        
        self.assertEqual(len(self.mmu.programsInMemory),0)
        self.assertEqual(len(self.mmu.programsInDisk),1)
        self.assertEqual(len(self.mmu.disk.disk.keys()),1)
        
        
class TestMemory(TestCase):
    
    def setUp(self):        
        self.memory = Memory()
    
    def testGet(self):
        try:
            self.memory.get(-1)
            self.fail()
        except OutOfMemoryBoundsException:
            try:
                self.memory.get(100)
                self.fail()
            except OutOfMemoryBoundsException:
                try:
                    self.memory.get(0)
                    self.fail()
                except KeyError:
                    print('OK')    
                    
class TestMMUPaginacion(TestCase):
    
    def setUp(self):
        self.mmu = MMUPaginacion(FIFO)
        cpu = Instr('CPU', 1)
        io = Instr('IO', 1)
        self.prog = Prog([cpu,cpu,cpu,cpu,cpu,cpu,cpu,io,cpu,cpu,cpu,io],'prog')
        
        self.pagina0 = Pagina([])
        self.pagina0.count = 1
        self.pagina0.base = 0
        self.pagina1 = Pagina([])
        self.pagina1.count = 7
        self.pagina2 = Pagina([])
        self.pagina2.count = 0
        self.paginas = [self.pagina0,self.pagina1,self.pagina2]
        self.mmu.colaDePaginasEnMemoria = self.paginas
        
    def testInicializarMarcos(self):
        libres = self.mmu.marcosLibres
        ocupados = self.mmu.marcosOcupados
        
        self.assertEqual(len(libres), 10)
        self.assertEqual(len(ocupados), 0)
        self.assertEqual(libres[0].base, 0)
        self.assertEqual(libres[1].base, 10)
        self.assertEqual(libres[2].base, 20)
        self.assertEqual(libres[3].base, 30)
        self.assertEqual(libres[4].base, 40)
        self.assertEqual(libres[5].base, 50)
        self.assertEqual(libres[6].base, 60)
        self.assertEqual(libres[7].base, 70)
        self.assertEqual(libres[8].base, 80)
        self.assertEqual(libres[9].base, 90)      
 
        
    def testCrearPaginas(self):
        self.mmu.crearPaginas(self.prog)
        
        paginas = self.mmu.tablaDePaginas['prog']
        pagina0 = paginas[0]
        pagina1 = paginas[1]
        
        self.assertEqual(len(paginas), 2)
        self.assertEqual(len(pagina0.instrucciones), 10)
        self.assertEqual(len(pagina1.instrucciones), 2)
        
    def testCargarPaginaEnMemoria(self):
        self.mmu.memory = MockMemory()
        self.mmu.crearPaginas(self.prog)        
        paginas = self.mmu.tablaDePaginas['prog']
        pagina0 = paginas[0]
        pagina1 = paginas[1]
        
        base1 = self.mmu.cargarPaginaEnMemoria(pagina0)
        base2 = self.mmu.cargarPaginaEnMemoria(pagina1)
        
        self.assertEqual(len(self.mmu.marcosLibres), 8)
        self.assertEqual(len(self.mmu.marcosOcupados), 2)
        self.assertEqual(base1, 0)
        self.assertEqual(base2, 10)
        self.assertEqual(self.mmu.memory.verify, 12)
        
    def testPaginaDeInstr(self):
        paginas = [0,1,2,3,4,5,6,7,8,9]
        pc = 45
        n = self.mmu.paginaDeInstr(paginas, pc)
        
        self.assertEqual(n,4)
        
    def testActualizarDatosDePagina(self):
        pagina = Pagina([])
        self.mmu.actualizarDatosDePagina(pagina)
        
        self.assertTrue(pagina.time - time.time() < 1)
        self.assertEqual(pagina.count, 1)
        self.assertEqual(len(self.mmu.pilaDePaginasEnMemoria), 1)
        
    def testLRU(self):
        pagina0 = Pagina([])
        time.sleep(0.1)
        pagina1 = Pagina([])
        time.sleep(0.1)
        pagina2 = Pagina([])        
        paginas = [pagina2,pagina0,pagina1]
        self.mmu.colaDePaginasEnMemoria = paginas
        
        victima = LRU(self.mmu)
        
        self.assertEqual(victima, pagina0)
        
    def testLFU(self):        
        victima = LFU(self.mmu)
        
        self.assertEqual(victima, self.pagina2) 
        
    def testMFU(self):        
        victima = MFU(self.mmu)
        
        self.assertEqual(victima, self.pagina1) 
        
    def testLoadProg(self):
        (base,limit) = self.mmu.loadProg(self.prog)
        
        self.assertEqual(len(self.mmu.marcosLibres), 9)
        self.assertEqual(len(self.mmu.marcosOcupados), 1)
        self.assertEqual(len(self.mmu.tablaDePaginas), 1)
        self.assertEqual(len(self.mmu.colaDePaginasEnMemoria), 4)
        self.assertEqual(base, 0)
        self.assertEqual(limit, 11)
        
    def testDescargarPaginaDeMemoria(self):
        pagina = Pagina([])
        self.mmu.cargarPaginaEnMemoria(pagina)
        self.mmu.cargarPaginaEnMemoria(pagina)
        
        self.mmu.descargarPaginaDeMemoria(pagina)
        
        self.assertEqual(len(self.mmu.marcosOcupados),1)
        self.assertEqual(len(self.mmu.marcosLibres),9)
        
    def testSwap(self):      
        pagina = Pagina([])
        self.mmu.cargarPaginaEnMemoria(pagina)
        self.mmu.cargarPaginaEnMemoria(pagina)
          
        base = self.mmu.swap(pagina)
        
        self.assertEqual(len(self.mmu.colaDePaginasEnMemoria),5)
        self.assertFalse(self.pagina0.enMemoria)
        self.assertTrue(pagina.enMemoria)
        self.assertEqual(base,20)        
        
        
        
class MockMemory:
    
    def __init__(self):
        self.verify = 0
    
    def put(self,intr,dire):
        self.verify += 1
        
        
        
            
    

#suite = TestLoader().loadTestsFromTestCase(TestFIFOScheduler)
#TextTestRunner(verbosity=2).run(suite)
    
    
